package com.ityuwen.mengyagushi.album.service.impl;

import com.ityuwen.mengyagushi.album.mapper.AlbumInfoMapper;
import com.ityuwen.mengyagushi.album.mapper.TrackInfoMapper;
import com.ityuwen.mengyagushi.album.mapper.TrackStatMapper;
import com.ityuwen.mengyagushi.album.service.TrackInfoService;
import com.ityuwen.mengyagushi.album.service.VodService;
import com.ityuwen.mengyagushi.common.constant.SystemConstant;
import com.ityuwen.mengyagushi.common.execption.GuiguException;
import com.ityuwen.mengyagushi.common.result.Result;
import com.ityuwen.mengyagushi.common.result.ResultCodeEnum;
import com.ityuwen.mengyagushi.common.util.AuthContextHolder;
import com.ityuwen.mengyagushi.model.album.AlbumInfo;
import com.ityuwen.mengyagushi.model.album.TrackInfo;
import com.ityuwen.mengyagushi.model.album.TrackStat;
import com.ityuwen.mengyagushi.model.user.UserInfo;
import com.ityuwen.mengyagushi.model.user.UserPaidTrack;
import com.ityuwen.mengyagushi.query.album.TrackInfoQuery;
import com.ityuwen.mengyagushi.user.client.UserInfoFeignClient;
import com.ityuwen.mengyagushi.vo.album.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

	@Autowired
	private TrackInfoMapper trackInfoMapper;

	@Autowired
	private TrackStatMapper trackStatMapper;

	@Autowired
	private AlbumInfoMapper albumInfoMapper;

	@Autowired
	private UserInfoFeignClient userInfoFeignClient;

	@Autowired
	private VodService vodService;

	@Transactional
	@Override
	public void saveTrackInfo(TrackInfoVo trackInfoVo) {
		// 1.保存声音
		TrackInfo trackInfo = new TrackInfo();
		BeanUtils.copyProperties(trackInfoVo, trackInfo);
		// 设置userId
		Long userId = AuthContextHolder.getUserId();
		trackInfo.setUserId(userId != null ? userId : 1L);
		// 设置声音的序列号：查询同一个专辑下目前最大的序列号，进行加1
		TrackInfo preTrack = this.getOne(new LambdaQueryWrapper<TrackInfo>()
				.eq(TrackInfo::getAlbumId, trackInfoVo.getAlbumId())
				.orderByDesc(TrackInfo::getOrderNum)
				.last("limit 1")
				.select(TrackInfo::getOrderNum));
		// 判断声音是否为空，为空则直接1，否则取orderNum + 1
		trackInfo.setOrderNum(preTrack == null ? 1 : preTrack.getOrderNum() + 1);
		// 获取媒体信息
		TrackMediaInfoVo mediaInfo = this.vodService.getMediaInfo(trackInfoVo.getMediaFileId());
		if (mediaInfo != null) {
			trackInfo.setMediaType(mediaInfo.getType());
			trackInfo.setMediaDuration(new BigDecimal(mediaInfo.getDuration()));
			trackInfo.setMediaUrl(mediaInfo.getMediaUrl());
			trackInfo.setMediaSize(mediaInfo.getSize());
		}
		trackInfo.setSource(SystemConstant.TRACK_SOURCE_USER);
		trackInfo.setStatus(SystemConstant.TRACK_STATUS_PASS);
		this.save(trackInfo);
		Long trackId = trackInfo.getId();

		// 2.更新专辑中的声音数量
		AlbumInfo albumInfo = this.albumInfoMapper.selectById(trackInfoVo.getAlbumId());
		if (albumInfo != null) {
			albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() + 1);
			this.albumInfoMapper.updateById(albumInfo);
		}

		// 3.初始化声音统计信息
		initTrackStat(trackId, SystemConstant.TRACK_STAT_PLAY);
		initTrackStat(trackId, SystemConstant.TRACK_STAT_COLLECT);
		initTrackStat(trackId, SystemConstant.TRACK_STAT_PRAISE);
		initTrackStat(trackId, SystemConstant.TRACK_STAT_COMMENT);
	}

	@Override
	public Page<TrackListVo> findUserTrackPage(Integer page, Integer limit, TrackInfoQuery query) {

		Long userId = AuthContextHolder.getUserId();
		query.setUserId(userId != null ? userId : 1L);
		return this.trackInfoMapper.findUserTrackPage(new Page<TrackListVo>(page, limit), query);
	}

	@Transactional
	@Override
	public void updateTrackInfo(Long trackId, TrackInfoVo trackInfoVo) {

		// 更新声音信息表
		TrackInfo trackInfo = new TrackInfo();
		BeanUtils.copyProperties(trackInfoVo, trackInfo);
		trackInfo.setId(trackId);
		// 媒体信息的重新处理：可能会重新上传新的声音
		TrackMediaInfoVo mediaInfo = this.vodService.getMediaInfo(trackInfoVo.getMediaFileId());
		if (mediaInfo != null) {
			trackInfo.setMediaType(mediaInfo.getType());
			trackInfo.setMediaDuration(new BigDecimal(mediaInfo.getDuration()));
			trackInfo.setMediaUrl(mediaInfo.getMediaUrl());
			trackInfo.setMediaSize(mediaInfo.getSize());
		}
		this.updateById(trackInfo);

	}

	@Transactional
	@Override
	public void removeTrackInfo(Long trackId) {

		TrackInfo trackInfo = this.trackInfoMapper.selectById(trackId);
		if (trackInfo == null) {
			return;
		}
		// 1.删除声音表记录
		this.removeById(trackId);

		// 2.更新专辑中包含声音数
		AlbumInfo albumInfo = this.albumInfoMapper.selectById(trackInfo.getAlbumId());
		albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() - 1);
		this.albumInfoMapper.updateById(albumInfo);

		// 3.删除声音统计信息
		this.trackStatMapper.delete(new LambdaUpdateWrapper<TrackStat>().eq(TrackStat::getTrackId, trackId));

		// 4.删除腾讯云点播中的媒体文件
		this.vodService.removeMedia(trackInfo.getMediaFileId());
	}

	@Override
	public Page<AlbumTrackListVo> findAlbumTrackPage(Long albumId, Integer page, Integer limit) {

		// 1.根据专辑id分页查询声音列表
		Page<AlbumTrackListVo> albumTrackListVoPage = this.trackInfoMapper.findAlbumTrackPage(new Page<>(page, limit), albumId);
		List<AlbumTrackListVo> trackListVos = albumTrackListVoPage.getRecords();
		if (CollectionUtils.isEmpty(trackListVos)){
			return albumTrackListVoPage;
		}

		// 2.根据专辑id查询专辑
		AlbumInfo albumInfo = this.albumInfoMapper.selectById(albumId);
		Assert.notNull(albumInfo, "分页查询声音列表时：获取专辑信息失败！");
		if (SystemConstant.ALBUM_PAY_TYPE_FREE.equals(albumInfo.getPayType())){
			return albumTrackListVoPage;
		}

		// 3.获取收费的声音列表
		Integer freeCount = albumInfo.getTracksForFree();
		// 需要收费的声音列表
		List<AlbumTrackListVo> needPaidTracks = trackListVos.stream().filter(track -> track.getOrderNum() > freeCount).collect(Collectors.toList());
		if(CollectionUtils.isEmpty(needPaidTracks)){
			return albumTrackListVoPage;
		}

		// 4.获取登录用户信息
		Long userId = AuthContextHolder.getUserId();
		if (userId != null){
			// 查询用户信息
			Result<UserInfo> userInfoResult = this.userInfoFeignClient.getUserInfo(userId);
			Assert.notNull(userInfoResult, "分页查询声音列表时：获取用户信息失败！");
			UserInfo userInfo = userInfoResult.getData();
			Assert.notNull(userInfoResult, "分页查询声音列表时：获取用户信息失败！");
			// 如果是未过期vip并且是vip免费专辑时，则直接返回结果
			if (userInfo.getIsVip() == 1 && userInfo.getVipExpireTime().after(new Date())
					&& SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(albumInfo.getPayType())){
				return albumTrackListVoPage;
			}

			// 5.查询用户是否购买了当前专辑
			Result<Boolean> paidAlbumResult = this.userInfoFeignClient.isPaidAlbum(albumId);
			Assert.notNull(paidAlbumResult, "分页查询声音列表时：获取是否购买专辑记录失败！");
			Boolean isPaid = paidAlbumResult.getData();
			if (isPaid){
				return albumTrackListVoPage;
			}

			// 6.查询用户购买当前专辑的声音列表
			Result<List<UserPaidTrack>> paidTracksResult = this.userInfoFeignClient.getPaidTracks(albumId);
			Assert.notNull(paidTracksResult, "分页查询声音列表时：获取已购买的声音列表失败！");
			List<UserPaidTrack> userPaidTracks = paidTracksResult.getData();
			if (CollectionUtils.isEmpty(userPaidTracks)){
				needPaidTracks.forEach(track -> track.setIsShowPaidMark(true));
				return albumTrackListVoPage;
			}

			// 7.如果已购买的声音列表不为空，遍历需要收费的声音列表，判断是否已购买
			List<Long> userPaidTrackIds = userPaidTracks.stream().map(UserPaidTrack::getTrackId).collect(Collectors.toList());
			needPaidTracks.forEach(track -> {
				if (!userPaidTrackIds.contains(track.getTrackId())) { // 如果当前声音不在已付费的列表中，则显示收费
					track.setIsShowPaidMark(true);
				}
			});

		} else {
			// 如果userId为空，说明未登录，需要收费就需要显示收费标签
			needPaidTracks.forEach(track -> track.setIsShowPaidMark(true));
		}

		return albumTrackListVoPage;
	}

	@Override
	public TrackStatVo getTrackStatVo(Long trackId) {

		return this.trackStatMapper.getTrackStatVo(trackId);
	}

	@Override
	public List<TrackOrderVo> findUserTrackPaidList(Long trackId) {
		// 根据声音id查询声音
		TrackInfo trackInfo = this.getById(trackId);
		if (trackInfo == null){
			throw new GuiguException(ResultCodeEnum.ILLEGAL_REQUEST);
		}

		// 查询同一专辑下，编号大于等于当前声音的声音列表
		List<TrackInfo> trackInfos = this.list(new LambdaQueryWrapper<TrackInfo>()
				.eq(TrackInfo::getAlbumId, trackInfo.getAlbumId())
				.ge(TrackInfo::getOrderNum, trackInfo.getOrderNum()));

		// 查询当前用户已购买的同一专辑下的声音列表
		Result<List<UserPaidTrack>> paidTracksResult = this.userInfoFeignClient.getPaidTracks(trackInfo.getAlbumId());
		Assert.notNull(paidTracksResult, "生成声音购买套餐时：获取已购买的声音失败！");
		List<UserPaidTrack> userPaidTracks = paidTracksResult.getData();
		List<TrackInfo> needTrackInfos = null;
		if (CollectionUtils.isEmpty(userPaidTracks)){
			needTrackInfos = trackInfos;
		} else {
			// 获取已购买的声音id集合
			List<Long> paidTrackIds = userPaidTracks.stream().map(UserPaidTrack::getTrackId).collect(Collectors.toList());
			// 过滤掉已购买的声音，获取未购买的声音集合
			needTrackInfos = trackInfos.stream().filter(track -> !paidTrackIds.contains(track.getId())).collect(Collectors.toList());
		}

		// 判断需要购买的声音列表是否为空
		if (CollectionUtils.isEmpty(needTrackInfos)){
			throw new GuiguException(ResultCodeEnum.ILLEGAL_REQUEST);
		}

		// 根据专辑id查询专辑信息获取声音单价
		AlbumInfo albumInfo = this.albumInfoMapper.selectById(trackInfo.getAlbumId());
		BigDecimal price = albumInfo.getPrice();

		// 组装套餐
		List<TrackOrderVo> trackOrderVos = new ArrayList<>();
		int count = needTrackInfos.size();
		// 本集：不管任何情况，都需要本集
		TrackOrderVo trackOrderVo = new TrackOrderVo();
		trackOrderVo.setName("本集");
		trackOrderVo.setTrackCount(1);
		trackOrderVo.setPrice(price);
		trackOrderVos.add(trackOrderVo);

		// 如果count大于10，一定会有后10集
		if (count > 10){
			TrackOrderVo orderVo = new TrackOrderVo();
			orderVo.setName("后10集");
			orderVo.setTrackCount(10);
			orderVo.setPrice(price.multiply(new BigDecimal(10)));
			trackOrderVos.add(orderVo);
		}
		// 如果count大于20，一定会有后20集
		if (count > 20){
			TrackOrderVo orderVo = new TrackOrderVo();
			orderVo.setName("后20集");
			orderVo.setTrackCount(20);
			orderVo.setPrice(price.multiply(new BigDecimal(20)));
			trackOrderVos.add(orderVo);
		}
		if (count > 30){
			TrackOrderVo orderVo = new TrackOrderVo();
			orderVo.setName("后30集");
			orderVo.setTrackCount(30);
			orderVo.setPrice(price.multiply(new BigDecimal(30)));
			trackOrderVos.add(orderVo);
		}
		if (count > 50){
			TrackOrderVo orderVo = new TrackOrderVo();
			orderVo.setName("后50集");
			orderVo.setTrackCount(50);
			orderVo.setPrice(price.multiply(new BigDecimal(50)));
			trackOrderVos.add(orderVo);
		}
		if (count > 1){
			TrackOrderVo orderVo = new TrackOrderVo();
			orderVo.setName("后" + count + "集");
			orderVo.setTrackCount(count);
			orderVo.setPrice(price.multiply(new BigDecimal(count)));
			trackOrderVos.add(orderVo);
		}

		return trackOrderVos;
	}

	@Override
	public List<TrackInfo> findBuyTracks(Long trackId, Integer count) {

		// 根据声音id查询声音（判断声音是否存在、获取专辑id）
		TrackInfo trackInfo = this.getById(trackId);
		if (trackInfo == null){
			return null;
		}

		// 查询该用户已购买的同一专辑下的声音列表
		Result<List<UserPaidTrack>> paidTracksResult = this.userInfoFeignClient.getPaidTracks(trackInfo.getAlbumId());
		Assert.notNull(paidTracksResult, "获取购买的声音列表时：查询已购买的声音列表失败！");
		List<UserPaidTrack> userPaidTracks = paidTracksResult.getData();

		// 构建查询条件
		LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<TrackInfo>()
				.eq(TrackInfo::getAlbumId, trackInfo.getAlbumId())
				.ge(TrackInfo::getOrderNum, trackInfo.getOrderNum())
				.last("limit " + count)
				.select(TrackInfo::getId, TrackInfo::getTrackTitle, TrackInfo::getCoverUrl);

		if (!CollectionUtils.isEmpty(userPaidTracks)){
			// 已购买的声音id集合
			List<Long> trackIds = userPaidTracks.stream().map(UserPaidTrack::getTrackId).collect(Collectors.toList());
			queryWrapper.notIn(TrackInfo::getId, trackIds);
		}

		return this.list(queryWrapper);
	}

	private void initTrackStat(Long trackId, String type) {
		TrackStat trackStat = new TrackStat();
		trackStat.setTrackId(trackId);
		trackStat.setStatType(type);
		trackStat.setStatNum(0);
		this.trackStatMapper.insert(trackStat);
	}
}
